
#include "BaseConvert.h"
#include "../iface/debug.h"
#include "../iface/SSLInterface.h"
#include "../aprtools/APRPRNG.h"
#include <openssl/evp.h>
#include "Hash.h"

#include "../aprtools/APRPRNG.h"

char *Hash::makeBase64UUID(int bytes)
{
   APRPRNG aprrng;
   char *ranbuf = aprrng.getRandomBytes(bytes);
   char *buf64 = BaseConvert::convertBytesToBase64(ranbuf, bytes);
   free(ranbuf);
   return buf64;
}

char *Hash::makeHexUUID_PlusUniq_deprecated(int bytes, KeyDB *kdb)
{
   while (1)
   {
      char *name = makeHexadecimalUUID(bytes);
      if (!(kdb->CheckForPublicKeyByName(name))) return name;
      free(name);
   }
}

char *Hash::makeHexUUID_PlusUniq2(int bytes, KeyDB *kdb, KeyDB *kdb2)
{
   while (1)
   {
      char *name = makeHexadecimalUUID(bytes);
      if ((!(kdb->CheckForPublicKeyByName(name))) &&
          (!(kdb2->CheckForPublicKeyByName(name))))
         return name;
      free(name);
   }
}

char *Hash::makeHexadecimalUUID(int bytes)
{
   APRPRNG aprrng;
   char *ranbuf = aprrng.getRandomBytes(bytes);
   char *bufhex = BaseConvert::convertBytesToHexadecimal(ranbuf, bytes);
   free(ranbuf);
   return bufhex;
}

char *Hash::getMD5Hash(const char *data, int length)
{
   unsigned char digest[EVP_MAX_MD_SIZE];
   char *md5hash = (char *)malloc(sizeof(char)*(EVP_MAX_MD_SIZE*2+1));
   md5hash[0] = '\0';
   EVP_MD_CTX *ctx;
   ctx = EVP_MD_CTX_create();
   const EVP_MD *md5 = EVP_md5();
   EVP_DigestInit_ex(ctx, md5, NULL);
   EVP_DigestUpdate(ctx, data, length);
   EVP_DigestFinal_ex(ctx, digest, NULL);
   EVP_MD_CTX_destroy(ctx);
   fSSLCheckError();
   for (int i = 0; i < md5->md_size; i++)
   {
      char hex[3];
      sprintf(hex, "%02x", digest[i]);
      strcat(md5hash, hex);
   }
   return md5hash;
}

char *Hash::getSHA1Hash(const char *data, int length)
{
   unsigned char digest[EVP_MAX_MD_SIZE];
   char *sha1hash = (char *)malloc(sizeof(char)*(EVP_MAX_MD_SIZE*2+1));
   sha1hash[0] = '\0';
   EVP_MD_CTX *ctx;
   ctx = EVP_MD_CTX_create();
   const EVP_MD *sha1 = EVP_sha1();
   EVP_DigestInit_ex(ctx, sha1, NULL);
   EVP_DigestUpdate(ctx, data, length);
   EVP_DigestFinal_ex(ctx, digest, NULL);
   EVP_MD_CTX_destroy(ctx);
   fSSLCheckError();
   for (int i = 0; i < sha1->md_size; i++)
   {
      char hex[3];
      sprintf(hex, "%02x", digest[i]);
      strcat(sha1hash, hex);
   }
   return sha1hash;
}

char *Hash::mergeBlockHashes(const char **hashes)
{
   const static char lookup_h2n[] = {  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, -1, -1, -1, -1, -1, -1,
                                      -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                                      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                                      -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
   const static char lookup_n2h[] = { '0', '1', '2', '3', '4', '5', '6', '7',
                                      '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
   int count = 0;
   if (hashes[0] == NULL) return NULL;
   int hashlen = strlen(hashes[0]);
   char *result = (char *)malloc(sizeof(char)*(hashlen+1));
   for (int i = 0; i < hashlen; i++) result[i] = '0';
   result[hashlen] = '\0';
   while (hashes[count] != NULL)
   {
      for (int i = 0; i < hashlen; i++)
      {
         int char1 = result[i];
         int char2 = (hashes[count])[i];
         int chr1_conv = (char1 < '0') ? -1 : ((char1 >= ('0' + 64)) ? -1 : lookup_h2n[char1 - '0']);
         int chr2_conv = (char2 < '0') ? -1 : ((char2 >= ('0' + 64)) ? -1 : lookup_h2n[char2 - '0']);
         if ((chr1_conv < 0) || (chr2_conv < 0))
         {
            errprint("invalid characters in hash value");
            return NULL;
         }
         result[i] = lookup_n2h[chr1_conv ^ chr2_conv];
      }
      count++;
   }
   return result;
}
